#include <msp430x16x.h>
#include <signal.h>

#include "coms.h"

#include "../main.h"
#include "../sd/sd.h"

/*
  interrupt(USART1RX_VECTOR) receivetrigger() triggers when receiving a character
  interrupt(USART1TX_VECTOR) transmittrigger() triggers once the transmit buffer empties
  comsInit(..) initialises the USART for COMS
  coms_send(..) sends a character to the modem and waits for a reply
  modInit(..) sends the modem setup commands
  modENQ0(..) checks the status of the modem
  modTransmit(..) sends a data character to the modem for transmission
*/


interrupt(USART1RX_VECTOR) receivetrigger() /* May need to implement start edge detection */
{
  unsigned char coms_error; /* Should be global */
  
  if(U1RCTL & RXWAKE) /* Address character received */
    mod_rec = 1;
  else /* Data character received */
    mod_rec = 2; 

  coms_error = U1RCTL & (FE | PE | OE | BRK | RXERR); /* Read error flags */
  mod_out = U1RXBUF; /* Read received character */
  
  U1RCTL &= ~URXWIE; /* Receive data and address characters */
}


interrupt(USART1TX_VECTOR) transmittrigger()
{
}


/* Type 1 = address, 2 = data */
int coms_send(unsigned char mod_in, int sendtype, int replytype, unsigned char replychar)
{
  unsigned long int mod_timeout;
  
  while((IFG2 & UTXIFG1) == 0) { } /* Wait until transmit buffer is empty */
  
  if(replytype == 1) /* Receive only address characters if recaddress=1 */
    U1RCTL |= URXWIE;
  else /* Receive only data characters if recaddress=0 */
    U1RCTL &= ~URXWIE;
  
  if(sendtype == 1) /* Next character sent is an address if address=1 */
    U1TCTL |= (TXWAKE);
  U1TXBUF = mod_in; /* Sends char mod_in */
  
  /* Wait for reply, currently ignores replies of wrong type */
  for(mod_timeout=0, mod_rec=0; ; mod_timeout++)
  {
    if(mod_rec == replytype) /* Check for correct character type */
      break;
    if(mod_timeout>MOD_TIMEOUT) /* If modem times out return error */
    {
      mod_bugcount+=1;
      return 2;
    }
  }
  mod_bugcount+=1;
  if(mod_out==MOD_WAKAK && replychar!=MOD_WAKAK) /* If reply is an unexpected WAKAK return error */
  {
    mod_bugcount+=1;
    return 3;
  }
  if(mod_out!=replychar) /* If not expected reply then return error */
  {
    mod_bugcount+=1;
    return 1;
  }
  
  return 0;
}

void comsInit(void)
{
  /* Set P3.6/P3.7 as transmit/receive pins */
  P3SEL |=  0xC0; /* P3.6/P6.7 to TXD data out / RXD data in */
  P3DIR |=  0x40; /* P3.6 to output */
  P3DIR &= ~0x80; /* P3.7 to input */

  /* Set P6.6 low to enable transceiver interface */
  P6SEL &= ~0x40; /* P6.6 to 3.3V I/O */
  P6DIR |=  0x40; /* P6.6 to output */
  P6OUT &= ~0x40; /* P6.6 to 0 */

  /* Set P4.6 low to enable +5V_SW power */
  P4SEL &= ~0x40; /* P4.6 to 3.3V I/O */
  P4DIR |=  0x40; /* P4.6 to output */
  P4OUT &= ~0x40; /* P4.6 to 0 */

  /* Initialise USART1 settings */
  U1CTL = (CHAR | SWRST | MM); /* Set SWRST, CHAR = 8 bit char length, MM = Address-Bit multiprocessor format */
  U1TCTL = SSEL1; /* Use SMCLK for BRCLK source */
  U1RCTL = URXEIE; /* Allow erroneous chars to be received */
  U1BR0 = 0x4F; /* Baud Rate - 9600Bd with 737,280Hz SMCLK */
  U1BR1 = 0x00; /* Baud Rate - 9600Bd with 737,280Hz SMCLK */
  U1MCTL = 0x4A; /* Modulation control - 9600Bd with 737,280Hz SMCLK */
  ME2 |= (UTXE1 | URXE1); /* Enables USART1 transmit/receive modules */
  U1CTL &= ~SWRST; /* Clear SWRST */
  IE2 |= URXIE1; /* Enables URXIFG1 interrupt */
  IE2 &= ~UTXIE1; /* Disables UTXIFG1 interrupt */
}


int modInit() /* Returns 1 = wrong character received, 2 = time out */
{  
  int i=0, status, mod_setup;
  unsigned char config[7] = { 0xCE, 0xA0, 0xB2, 0x80, 0x40, 0x80, 0x04 }; /* Config data from mod_int.py */
  
  mod_reinit=0; /* Reset modem init request flag */
  mod_bugcount=0; /* Reset bug count */
  
  for(mod_setup=0; ; mod_setup++)
  {    
    for(i=0; i<10000; i++) /* Delay at start to avoid errors */
      nop();
    
    /* Wake up the modem first */
    status = coms_send(MOD_WAKE,1,1,MOD_WAKAK);
    if(status!=0)
      return status;
  
    for(i=0; i<7; i++) /* Run through configuration of each setting */
    {   
      status = coms_send(i,1,1,MOD_ACK); /* Send CFG command character */
      if(status!=0)
        break;
    
      status = coms_send(config[i],0,1,MOD_ACK); /* Send config data character */
      if(status!=0)
        break;
    }
    if(status==1) /* Retry init if modem replies with the wrong character */
      continue;
    if(status==2) /* Retry init if modem times out */
      continue;
    
    status = modENQ0(); /* Enquire for status */
    if(status==1) /* Retry init if modem replies with the wrong character */
      continue; 
    if(status==2) /* Retry init if modem times out */
      continue; 
    
    if(mod_status & 0xF0) /* Check error bits */
      ; 
    else 
      break; /* Finish loop if there are no errors */
    
    /* Check modem status errors */
    if(mod_status & MOD_SYNCOUTLOCK) /* Synchroniser out of lock (error) */
      continue;
    if(mod_status & MOD_RXBUFOVER) /* RX buffer overflow (error) */
      continue;
    if(mod_status & MOD_TXBUFOVER) /* TX buffer overflow (error) */
      continue;
    if(mod_status & MOD_POWERONRST) /* Power on reset status (error) */
      continue;
    
    if(mod_setup>MOD_SETUPRETRY) /* If modem won't setup correctly too many times return error */
      return 1; 
  }
  
  return 0;
}


int modENQ0() /* Returns 1 = wrong character received, 2 = time out */
{
  int status, i;
  long int mod_timeout;

  for(i=0; i<5; i++) /* Keep trying to enquire up to 5 times */
  {   
    status = coms_send(MOD_ENQ0,1,1,MOD_RQD); /* Send enquire command */
    if(status!=0)
      continue;
    
    /* Wait for data */
    for(mod_timeout=0, mod_rec=0; mod_timeout<=MOD_TIMEOUT; mod_timeout++)
    {
      if(mod_rec == 2) /* Check for data character */
        break; 
    }  
      
    if(mod_timeout>MOD_TIMEOUT) /* Retry enquire if modem has timed out */
    {
      mod_bugcount+=1;
      status=2; 
    }
    
    break;
  }
  if(status==1) { /* If modem keeps replying with the wrong character */
    mod_reinit=1; /* Request rerun of modem init */
    return 1;
  }
  if(status==2)/* If modem keeps timing out */
  {
    mod_reinit=1; /* Request rerun of modem init */
    return 2;
  }
  
  mod_rec = 0; /* Reset data received flag so the main loop doesn't trigger a mode change */
  mod_status = mod_out; /* Store modem status globally */
  return 0;
}


/* Returns 1 = wrong reply, 2 = time out, 3 = multiple unexpected WAKAKS, 4 = modem error flags, 5 = transmitter not on */
int modTransmit(unsigned char *data, int arraysize) 
{
  int status, i, j, datasize;
  
  for(i=0; i<3; i++) /* Runs through 3 times if replying WAKAK, returns error for other problems */
  {
    status=coms_send(MOD_TXSTT,1,1,MOD_ACK); /* Set modem to transmission mode */
    if(status==3) continue; /* Retry for WAKAK */
    else if(status!=0) return status; 
    else break;
  }
  if(status==3) /* If the modem keeps returning WAKAK then request rerun of init */
  {
    mod_reinit=1;
    return 3;
  }
  
  for( i=0 ; i<5000 ; i++ ) nop(); /* Short delay, may not be required */
  
  status = modENQ0(); /* Enquire for modem status while in transmit mode */
  if(status!=0) return status;
  
  if(mod_status & MOD_TRANSMITON) ; /* Check that transmitter is on */
  else return 5;
  
  if(mod_status & 0xF0) /* If enquire returns any errors request modem init and return error */
  {
    mod_reinit=1;
    return 4;
  }
  
  for(i=0, j=0; i<arraysize; i++)
  {
    if(j==3) return 3; /* Allow 3 WAKAKS per char then terminate */
  
    status = coms_send(data[i],0,1,MOD_ACK); /* Send character as data */
    if(status==3) 
    {
      i--;
      j++;
      continue; /* Resend data if reply is WAKAK */
    }
    if(status!=0) return status;
    j=0;
  }
  
  for(i=0; i<2; i++) /* Runs through twice if reply is WAKAK, returns error for other problems */
  {
    status=coms_send(MOD_TXEND,1,1,MOD_ACK); /* End transmission mode */
    if(status==3) continue; /* Retry for WAKAK */
    else if(status!=0) return status; 
    else break;
  }
  if(status==3) /* If the modem keeps returning WAKAK then request rerun of init */
  {
    mod_reinit=1;
    return 3;
  }
  
  return 0;
}
